package overlays;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import XMLread.Space;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Paint.Style;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;

/**
 * A map overlay to draw the given floor of a building onto the map.
 */
public class SpaceAreasOverlay extends Overlay {
	private final Map<String, List<GeoPoint>> mAreas;
	private final Space startSpace;
	private final Space endSpace;
	private final Paint mPaintFill;
	private final Paint mPaintStroke;
	private int startSpaceIndex = -1;
	private int endSpaceIndex	= -1;


	/**
	 * Constructor for this overlay.
	 * @param areaMap the map of building points to draw.
	 */
	public SpaceAreasOverlay(final Map<String, List<GeoPoint>> areaMap, final Space sp) {
		mAreas = areaMap;
		endSpace = sp;
		startSpace = null;

		// prepare paints
		mPaintFill = new Paint();
		mPaintFill.setStyle(Paint.Style.FILL);
		mPaintStroke = new Paint(Paint.ANTI_ALIAS_FLAG);
		mPaintStroke.setStyle(Style.STROKE);
		mPaintStroke.setAntiAlias(true);
		mPaintStroke.setStrokeWidth(3);
	}

	/**
	 * Constructor for this overlay.
	 * @param areaMap the map of building points to draw.
	 * @param startSpace the starting space that should be highlighted on the starting building.
	 * @param endSpcace the space representing the destination allowing it to be highlighted.
	 */
	public SpaceAreasOverlay(Map<String, List<GeoPoint>> areas,
			Space startSpace, Space endSpace) {
		mAreas = areas;
		this.startSpace = startSpace;
		this.endSpace = endSpace;

		// prepare paints
		mPaintFill = new Paint();
		mPaintFill.setStyle(Paint.Style.FILL);
		mPaintStroke = new Paint(Paint.ANTI_ALIAS_FLAG);
		mPaintStroke.setStyle(Style.STROKE);
		mPaintStroke.setAntiAlias(true);
		mPaintStroke.setStrokeWidth(3);
	}

	@Override
	public void draw(final Canvas canvas, final MapView mapView, final boolean shadow) {
		super.draw(canvas, mapView, shadow);
		if (shadow) {
			return;
		}
		Projection projection = mapView.getProjection();

		List<Path> areaPaths = getPaths(projection, mAreas);
		drawPaths(canvas, areaPaths);
	}

	/**
	 * Retrieves the paths to draw based on the area map and projection.
	 * @param projection The projection of the map to draw the buildings onto.
	 * @param areas The map that contains the building shapes.
	 * @return the list of paths to be drawn.
	 */
	private List<Path> getPaths(final Projection projection, final Map<String, List<GeoPoint>> areas) {
		List<Path> areaPaths = new ArrayList<Path>(areas.size());
		for (Map.Entry<String, List<GeoPoint>> entry : areas.entrySet()) {
			List<GeoPoint> sourceList = entry.getValue();
			Path path = new Path();
			path.setFillType(Path.FillType.EVEN_ODD);
			Iterator<GeoPoint> it = sourceList.iterator();
			Point point = nextDrawPoint(projection, it);
			path.moveTo(point.x, point.y);
			while (it.hasNext()) {
				point = nextDrawPoint(projection, it);
				path.lineTo(point.x, point.y);
			}
			path.close();
			areaPaths.add(path);
			if(entry.getKey().equals(endSpace.getRoom())){
				endSpaceIndex = areaPaths.size()-1;
			}
			if(startSpace != null && entry.getKey().equals(startSpace.getRoom())){
				startSpaceIndex = areaPaths.size()-1;
			}
		}
		return areaPaths;
	}

	/**
	 * Draw with different colors.
	 * Draw strokes first for them to be always visible.
	 * Draw fills next with each removing from the drawable area.
	 */
	private void drawPaths(final Canvas canvas, final List<Path> areaPaths) {
		for (int i=0; i<areaPaths.size(); i++) {
			Path path = areaPaths.get(i);
			if(i==startSpaceIndex || i==endSpaceIndex){
				mPaintStroke.setColor(Color.RED);
				canvas.drawPath(path, mPaintStroke);
				mPaintFill.setColor(Color.YELLOW);
				canvas.drawPath(path, mPaintFill);
			}
			else{
				mPaintStroke.setColor(Color.GRAY);
				canvas.drawPath(path, mPaintStroke);
				mPaintFill.setColor(Color.LTGRAY);
				canvas.drawPath(path, mPaintFill);
			}
		}
	}

	/**
	 * Draws the next point based on the projection.
	 * @param projection The projection of the map to draw the point onto.
	 * @param it The iterator of points to draw.
	 * @return The last point drawn.
	 */
	private Point nextDrawPoint(final Projection projection, final Iterator<GeoPoint> it) {
		GeoPoint geo = it.next();
		Point p = new Point();
		projection.toPixels(geo, p);
		return p;
	}
}
